/* 
 *  Syntelos XB
 *  Copyright (C) 2006 John Pritchard.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License as
 *  published by the Free Software Foundation; either version 2 of
 *  the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 *  02111-1307 USA
 */

package syntelos.xb.op;

/**
 * <p> JVM instruction <code>0xaa tableswitch</code>. </p>
 */
public final class Tableswitch
    extends syntelos.xb.Op.Switch
{
    public final static syntelos.iou.dom.Name NAME = new syntelos.iou.dom.impl.Name(ELEMENT_NODE,"http://www.syntelos.org/xb/op","xop:tableswitch");
    public final static int code = CODE_TABLESWITCH;
    public final static java.lang.String name = "tableswitch";
    public final static int size_operands = 14;
    public final static int count_stack_pop = 1;
    public final static int count_stack_push = 0;


    /**
     * <p> Child of {@link syntelos.xb.Op} . </p>
     * 
     * @see syntelos.xb.at.Code
     */
    public final static class Case
	extends syntelos.xb.op.Operand.Case
    {
	public final static syntelos.iou.dom.Name NAME = new syntelos.iou.dom.impl.Name(ELEMENT_NODE,"http://www.syntelos.org/xb/op","xop:tableswitch.case");
    
	public Case(org.w3c.dom.Document doc, syntelos.iou.dom.Name name){
	    super(doc,name);
	}
	public Case(org.w3c.dom.Document doc){
	    this(doc,NAME);
	}
	public Case(){
	    this(DOC_NIL);
	}

    }

    /**
     * Dense, contiguous match set
     */
    protected final static class Children
	extends syntelos.iou.dom.impl.NodeList
    {

	protected Children(Tableswitch parent){
	    super(parent);
	}

	public Case getCaseByKey( int key, int low, int high){
	    int idx = (key - low);
	    return (Case)this.item(idx);
	}
	public Case getCaseByIndex( int idx){
	    return (Case)this.item(idx);
	}
	public void readCF(int count, syntelos.xb.Op.Reader in)
	    throws java.io.IOException
	{
	    Case switchcase;
	    for (int cc = 0; cc < count; cc++){
		switchcase = new Case();
		this.add(switchcase);
		switchcase.readCF(in);
	    }
	}
	public void writeCF(syntelos.iou.bbuf out){
	    int count = this.getLength();
	    Case switchcase;
	    for (int cc = 0; cc < count; cc++){
		switchcase = this.getCaseByIndex(cc);
		switchcase.writeCF(out);
	    }
	}
	public void add(Case switchcase){

	    this.append(switchcase);
	}
    }



    public Tableswitch(org.w3c.dom.Document doc, syntelos.iou.dom.Name type){
        super(doc,type);
    }
    public Tableswitch(){
        super(NAME,false);
    }
    public Tableswitch(boolean shared){
        super(NAME,true);
    }


    protected syntelos.iou.dom.NodeList newChildNodes(){
	return new Children(this);
    }
    protected Children getChildren(){
	return (Children)this.getChildNodes2();
    }
    public syntelos.iou.dom.Attr getOperandLowAttr(){
	return this.getCreateAttributeNode("low");
    }
    public int getOperandLow(){
	syntelos.iou.dom.Attr node = this.getOperandLowAttr();
	java.lang.Integer number = (java.lang.Integer)node.getData(syntelos.xb.IO.Data.INTEGER,this);
	if (null != number)
	    return number.intValue();
	else
	    throw new syntelos.xb.Error.Bug();
    }
    public void setOperandLow(int value){
	syntelos.iou.dom.Attr node = this.getOperandLowAttr();
	java.lang.Integer number = new java.lang.Integer(value);
	node.setData(number,this);
    }
    public syntelos.iou.dom.Attr getOperandHighAttr(){
	return this.getCreateAttributeNode("high");
    }
    public int getOperandHigh(){
	syntelos.iou.dom.Attr node = this.getOperandHighAttr();
	java.lang.Integer number = (java.lang.Integer)node.getData(syntelos.xb.IO.Data.INTEGER,this);
	if (null != number)
	    return number.intValue();
	else
	    throw new syntelos.xb.Error.Bug();
    }
    public void setOperandHigh(int value){
	syntelos.iou.dom.Attr node = this.getOperandHighAttr();
	java.lang.Integer number = new java.lang.Integer(value);
	node.setData(number,this);
    }
    public syntelos.xb.op.Operand.Case getOperandCaseNode(int key){
	int low = this.getOperandLow();
	int high = this.getOperandHigh();
	return this.getChildren().getCaseByKey(key,low,high);
    }
    public syntelos.xb.op.Operand.Case getCreateOperandCaseNode(int key){
	if (this.hasChildNodes()){
	    int high = this.getOperandHigh();
	    int low = this.getOperandLow();
	    int test = java.lang.Math.min(low,key);
	    if (test != low){
		int old_low = low;
		low = test;
		if ((old_low-1) == low){
		    this.setOperandLow(low);
		    Children children = this.getChildren();
		    Case switchcase = new Case();
		    Case existing = children.getCaseByIndex(0);
		    children.insert(switchcase,existing);
		    switchcase.setId(key);
		    return switchcase;
		}
		else
		    throw new syntelos.xb.Error.Argument("Key ("+key+") is not the decrement (-1) of the low case key ("+old_low+").");
	    }
	    else {
		test = java.lang.Math.max(high,key);
		if (test != high){
		    int old_high = high;
		    high = test;
		    if ((old_high+1) == high){
			this.setOperandHigh(high);
			Case switchcase = new Case();
			this.appendChild(switchcase);
			switchcase.setId(key);
			return switchcase;
		    }
		    else
			throw new syntelos.xb.Error.Argument("Key ("+key+") is not the increment (+1) of the high case key ("+old_high+").");
		}
		else {
		    int idx = (key - low);
		    Children children = this.getChildren();
		    Case existing = children.getCaseByIndex(idx);
		    return existing;
		}
	    }
	}
	else {
	    int high = key;
	    this.setOperandHigh(high);
	    int low = key;
	    this.setOperandLow(low);
	    int idx = 0;
	    Case switchcase = new Case();
	    this.appendChild(switchcase);
	    switchcase.setId(key);
	    return switchcase;
	}
    }
    public int getCode(){
        return code;
    }
    public java.lang.String getName(){
        return name;
    }
    public int sizeOperands(){
	int acc = Pad(this.getPC());
	acc += 12;
	int jumptable_len = this.getChildNodes2().getLength();
	acc += (4 * jumptable_len);
	return acc;
    }
    public int countStackPop(){
        return count_stack_pop;
    }
    public int countStackPush(){
        return count_stack_push;
    }
    public void readCF(syntelos.xb.Class clas, syntelos.xb.Method meth, syntelos.xb.Op.Reader in)
        throws java.io.IOException
    {
        if (this.setPC(in)){
	    int pc = this.getPC();
	    in.mark(java.lang.Integer.MAX_VALUE);
	    int pad = Pad(pc);
	    in.skip(pad);
	    this.getOperandCaseDefaultAttr().readCF(in);
	    int operand_low = in.readInt();
	    this.setOperandLow(operand_low);
	    int operand_high = in.readInt();
	    this.setOperandHigh(operand_high);
	    int count = (operand_high - operand_low) + 1;
	    this.getChildren().readCF(count,in);
	    /*
	     * Save input bits 
	     */
	    byte[] cf = in.marked();
	    this.setCF(cf);
        }
        else
            throw new syntelos.xb.Error.Bug();
    }
    public void writeCF(syntelos.xb.Class clas, syntelos.xb.Method meth, syntelos.xb.Op.Writer out)
        throws java.io.IOException
    {
        if (this.setPC(out)){
	    int pc = this.getPC();
	    int pad = Pad(pc);
	    int operand_low = this.getOperandLow();
	    int operand_high = this.getOperandHigh();
	    int count = (operand_high - operand_low)+1;
	    syntelos.iou.bbuf cf_buf = new syntelos.iou.bbuf();
	    cf_buf.skip_write(pad);
	    this.getOperandCaseDefaultAttr().writeCF(cf_buf);
	    cf_buf.write4(operand_low);
	    cf_buf.write4(operand_high);
	    this.getChildren().writeCF(cf_buf);
	    /*
	     * Save output bits 
	     */
	    byte[] cf = cf_buf.toByteArray();
	    this.setCF(cf);
	    out.write(cf);
        }
        else
            throw new syntelos.xb.Error.Bug();
    }
}
